【SwiftUI】TextFieldの文字数を制限したりスペースを入力させないようにする
TextField
で入力フォームを作る際に、文字数を制限したり、スペースを入力してほしくない時がいつかあると思います。そんな時に備えて調べてみました。
環境
- Xcode 13.2.1
Combineを使う
今回はiOS 13以降で使える非同期フレームワークのCombineを使用します。
import Combine
TextFieldの準備
今回はパスワード入力フォームという前提で進めていきます。入力出来る最大文字数の定数maxPasswordLength
も準備しておきました。
import SwiftUI import Combine struct ContentView: View { @State private var passwordText = "" private let maxPasswordLength = 4 var body: some View { TextField("パスワード", text: $passwordText) .padding() .border(.black) .padding() } }
入力文字のイベントを購読する
TextField
の入力イベントを購読する為に、onRecieve
メソッドを使って、パスワードの入力文字を変更を検知できるようにします。
onReceive(_:perform:)
第一引数には、Publisherを渡して、そのPublisherからのイベント発行がある度に処理を実行してくれます。
func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never
onReceive
でイベント発行を受け取るには、passwordText
をPublisherに変更してあげる必要がある為、Just
を使用して変更します。
Just
Just
はCombineフレームワークの構造体で、Subscriber(今回でいうonRecieve
)に出力を送信出来るPublisherです。
これでpasswordText
の出力がある度に何らかの処理を実行出来るようになりました。
TextField("パスワード", text: $passwordText) .padding() .border(.black) .padding() .onReceive(Just(passwordText)) { _ in // 実行したい処理 }
_
の箇所は、出力結果として、今回はpasswordText
の値が入ってくるのですが、今回はインスタンスプロパティのpasswordText
の値と変わらない為、使用しない方向で対応しました。
最大文字数を超えた文字を切り捨てる
passwordText
からイベント発行出来るようになったので、後は最大文字数を超えた文字を切り捨てるだけです。
.onReceive(Just(passwordText)) { _ in if passwordText.count > maxPasswordLength { passwordText = String(passwordText.prefix(maxPasswordLength)) } }
passwordText
の文字数がmaxPasswordLength
を超える場合は、それ以降の文字を切り捨てたものをpasswordText
に代入しています。
スペースを削除する
半角と全角のスペースを文字列から取り除くextensionメソッドを作ってみました。
extension String { func removingWhiteSpace() -> String { let whiteSpaces: CharacterSet = [" ", " "] return self.trimmingCharacters(in: whiteSpaces) } }
removingWhiteSpace
メソッドを使用して、文字列から全角半角スペースを取り除いた値を取得します。
passwordText = passwordText.removingWhiteSpace()
出来たもの
デモ
コード
import SwiftUI import Combine struct ContentView: View { @State private var passwordText = "" private let maxPasswordLength = 4 var body: some View { TextField("パスワード", text: $passwordText) .padding() .border(.black) .padding() .onReceive(Just(passwordText)) { _ in if passwordText.count > maxPasswordLength { // 最大文字数超えた場合は切り捨て passwordText = String(passwordText.prefix(maxPasswordLength)) } // 文字列から全角半角スペースを取り除く passwordText = passwordText.removingWhiteSpace() } } }
おわりに
本来のパスワードの場合は、最大文字数がもっと多かったり、もっと複雑な入力文字規制があると思います。この方法を応用すると出来そうですね。
何はともあれ、安全なパスワードを運用を心がけましょう。